#include "includes.h"

/* Compute Degree of Membership
 * Degree to which input is a member of mf is calculated as follows: 
 * 1. Compute delta terms to determine if input is inside or outside 
 * membership function. 
 * 2. If outside, then degree of membership is 0. Otherwise, smaller of 
 * delta_1 * slope1 and delta_2 * slope2 applies.
 * 3. Enforce upper limit.
 */
void compute_degree_of_membership(f_mf_type *mf, double input){
    double delta_1,delta_2;
    delta_1=input-mf->point1;
    delta_2=mf->point2-input;
    if((delta_1<=0)||(delta_2<=0))   /* input outside mem. function ?  */
        mf->value=0;                 /* then degree of membership is 0 */
    else
        mf->value=MIN((mf->slope1*delta_1), (mf->slope2*delta_2));
    mf->value=MIN(mf->value, UPPER_LIMIT);  /* enforce upper limit */
}

/* Compute Area of Trapezoid
 * Each inference pass produces a new set of output strengths which affect 
 * the areas of trapezoidal membership functions used in center-of-gravity 
 * defuzzification. Area values must be recalculated with each pass. Area of 
 * trapezoid is h*(a+b)/2 where h=height=output_strength=mf->value 
 * b=base=mf->point2-mf->point1 a=top= must be derived from h,b, and slopes1&2
 */
double compute_area_of_trapezoid(f_mf_type *mf){
    double run_1,run_2;
    double base,top,area;
    base=mf->point2-mf->point1;
    run_1=mf->value/mf->slope1;
    run_2=mf->value/mf->slope2;
    top=base-run_1-run_2;
    area=mf->value*(base+top)/2;
    return area;
}

/* Fuzzification 
 * Degree of membership value is calculated for each membership
 * function of each system input. Values correspond to antecedents in rules.
 */
void fuzzification(f_io_type *sysinput){
    f_io_type *si;    /* system input pointer        */
    f_mf_type *mf;    /* membership function pointer */
    for(si=sysinput; si!=NULL; si=si->next)
        for(mf=si->memf; mf!=NULL; mf=mf->next)
            compute_degree_of_membership(mf, si->value);
}

/* Rule Evaluation 
 * Each rule consists of a list of pointers to antecedents 
 * (if side), list of pointers to outputs (then side), and pointer to next rule
 * in rule base. When a rule is evaluated, its antecedents are ANDed together,
 * using a minimum function, to form strength of rule. Then strength is applied
 * to each of listed rule outputs. If an output has already been assigned a rule
 * strength, during current inference pass, a maximum function is used to
 * determine which strength should apply.
 */
void rule_evaluation(f_rule_type *rulebase){
    f_rule_type *rule;
    f_rule_elemtype *ip;       /* pointer of antecedents  (if-parts)   */
    f_rule_elemtype *tp;       /* pointer to consequences (then-parts) */
    double strength;                /* strength of  rule currently being evaluated */
    for(rule=rulebase; rule!=NULL; rule=rule->next){
        strength=UPPER_LIMIT;                       /* max rule strength allowed */
        /* process if-side of rule to determine strength */
        for(ip=rule->if_side; ip!=NULL; ip=ip->next)
            strength=MIN(strength, *(ip->value));
       /* process then-side of rule to apply strength */
        for(tp=rule->then_side; tp!=NULL; tp=tp->next)
            *(tp->value)=MAX(strength, *(tp->value));
    }
}

/* Defuzzification */
void defuzzification(f_io_type *sysoutput){
    f_io_type *so;    /* system output pointer                  */
    f_mf_type *mf;    /* output membership function pointer     */
    double sum_of_products;   /* sum of products of area & centroid */
    double sum_of_areas;     /* sum of shortend trapezoid area          */
    double area,centroid;
    /* compute a defuzzified value for each system output */
    for(so=sysoutput; so!=NULL; so=so->next){
        sum_of_products=0;
        sum_of_areas=0;
        for(mf=so->memf; mf!=NULL; mf=mf->next){
            area=compute_area_of_trapezoid(mf);
            centroid=mf->point1+(mf->point2-mf->point1)/2;
            sum_of_products+=area*centroid;
            sum_of_areas+=area;
        }
        so->value=sum_of_products/sum_of_areas;   /* weighted average */
    }
}

/* re-initialize then-side values */
void fuzzy_refinement(f_rule_type *rulebase){
    f_rule_type *rule;
    f_rule_elemtype *tp;
    for(rule=rulebase; rule!=NULL; rule=rule->next){
        for(tp=rule->then_side; tp!=NULL; tp=tp->next)
            *(tp->value)=0;
    }
}
